﻿using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

[assembly: InternalsVisibleTo("TST_ALL")]

namespace StockInfoFacade.Utils
{
    
    internal class PCQueue : IDisposable
    {
        readonly object _locker = new object();
        Thread[] _workers;
        Queue<Action> _itemQ = new Queue<Action>();
        private bool _exitAll = false;

        public PCQueue(int workerCount)
        {
            _workers = new Thread[workerCount];
            // Create and start a separate thread for each worker
            for (int i = 0; i < workerCount; i++)
                (_workers[i] = new Thread(Consume)).Start();
        }

        public void Shutdown(bool waitForWorkers)
        {
            // Enqueue one null item per worker to make each exit.
            foreach (Thread worker in _workers)
                EnqueueItem(null);
            // Wait for workers to finish
            if (waitForWorkers)
                foreach (Thread worker in _workers)
                    worker.Join();
        }

        public void EnqueueItem(Action item)
        {
            lock (_locker)
            {
                _itemQ.Enqueue(item); // We must pulse because we're
                Monitor.Pulse(_locker); // changing a blocking condition.
            }
        }

        private void Consume()
        {
            while (true) // Keep consuming until
            { // told otherwise.
                Action item;
                lock (_locker)
                {
                    while (_itemQ.Count == 0)
                    {
                        if (!_exitAll)
                            break;
                        Monitor.Wait(_locker);
                    }
                    if (_itemQ.Count > 0) 
                        item = _itemQ.Dequeue();
                    else
                        return;
                }
                if (item == null) return; // This signals our exit. Catastrophic exit, no method provided.
                item(); // Execute item.
            }
        }

        // TODO: not smoothly. will redo in future
        public void WaitTillEmptyQueue()
        {
            while (_workers.ToList().Exists(t => t.ThreadState == ThreadState.Running))
            {
                Thread.Sleep(100);
            }
        }


        public void Dispose()
        {
            WaitTillEmptyQueue();
            // exit works
            _exitAll = true;
            lock (_locker)
            {
                Monitor.PulseAll(_locker);
            }
            // wait till everybody out
            while (_workers.ToList().Exists(t => t.ThreadState == ThreadState.Running || t.ThreadState == ThreadState.WaitSleepJoin))
            {
                Thread.Sleep(100);
            }


        }
    }
}
